home *** CD-ROM | disk | FTP | other *** search
- /*
- * Sample reinstallable init
- * v 1.0
- *
- * August 1993 Greg Robbins
- *
- * This sample INIT patches a trap globally yet is reinstallable:
- * it can be recompiled and run without rebooting.
- *
- * Usually, when an INIT patches a trap, changing the INIT requires
- * reinstalling the INIT in the Extensions folder and rebooting.
- * This INIT demonstrates a technique which allows new INIT code to
- * replace most of the old code without the developer having to reboot.
- *
- * This INIT just patches Standard File (_Pack3) and beeps when
- * a standard file dialog is raised.
- *
- * Demonstration:
- *
- * • put the Reinstallable Init in the Extensions folder and reboot
- * • bring up a standard file dialog; notice the beep
- * • drag the Reinstallable twoBeep INIT file onto the
- * LaunchInits program
- * • bring up a standard file dialog again and notice both beeps
- *
- * The trick is to keep a global handle in the system heap containing
- * the addresses of the routines we want executed for each
- * patched item. The handle to the globals is available via Gestalt.
- *
- * A permanently installed dispatcher routine (part of the init code)
- * is used to get the address of our patch routine and jump to it.
- *
- * The second time the init code runs, it will use the existing global
- * handle, and just update the addresses of our patch routines so the
- * dispatcher code jumps to the correct places.
- *
- * A couple of critical items:
- * - The init loaded at boot time contains the patch dispatchers, so
- * it must never be disposed of. We will simply strand it in the
- * heap when the first reinstallation occurs.
- * - If we change anything about which routines are patched, this invalidates
- * the global handle struct format, so we must reboot
- * - Similarly, if the dispatching code is changed, we must reboot
- * since only the dispatching code from the first time the init is
- * installed can be used.
- * - The dispatch and patch routines destroy registers, so they
- * may not be suitable for OS patches
- *
- * Globals:
- *
- * Since this code only uses a single true global (a handle to our
- * globals in the system heap) I used a simpler method than Think C's
- * A4 globals. The global handle is stored smack in the middle of the
- * GetGlobalsHandleFromStorage routine. Calling GGHFS with a handle
- * stores the handle there; calling GGHFS with no parameter returns
- * the stored handle. (The handle can also be retrieved from
- * Gestalt, but that is too slow a method for use in a trap patch.)
- *
- */
-
- #define SystemSevenOrLater 1 // leave out unnecessary glue code
-
- #include <Memory.h>
- #include <GestaltEqu.h>
- #include <StandardFile.h>
- #include <Resources.h>
- #include <Events.h>
- #include <SysEqu.h>
- #include <Traps.h>
-
- #define kOptionKeyCode 0x3A
-
- #define kReinstInitSelector 'Réiñ' // something suitably obscure
-
- // global types
-
- typedef struct Globals {
- Size globalsSize; // size of this struct
- Handle initHandle; // handle for this INIT if reinstalled
-
- // traps and hooks
- ProcPtr pack3TrueAddr; // old trap address
- ProcPtr pack3PatchAddr; // my patch routine address
-
- ProcPtr reinstInitGestaltFunctionAddr;
- // address of my Gestalt function
-
- // other globals can go here
-
- } Globals, *GlobalsPtr, **GlobalsHandle;
-
- // prototypes
-
- Boolean KeyIsDown(short);
- GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle);
- void SetGlobalsHandleToStorage(GlobalsHandle *);
- void ReinstInitGestaltDispatch(void);
- void Pack3PatchDispatch(void);
- pascal OSErr ReinstInitGestaltFunction (OSType, long *);
- void Pack3Patch(void);
-
-
-
- // installation routine
-
- main()
- {
-
- Handle initHandle, previousInitHandle;
- GlobalsHandle gHandle;
- Boolean installedFlag, firstInstallFlag;
- OSErr retCode;
- long gestaltResponse;
-
- // Think C points A0 at the start of the block;
- // use it to get the handle to the INIT resource
- asm {
- RecoverHandle
- MOVE.L A0, initHandle
- }
-
- // never hurts to lock the block of code we are in
- HLock(initHandle);
-
- // since this INIT uses the "lazy" installation method
- // (it's just a detached resource) it's critical that
- // the INIT resource had its system heap bit
- // set. I won't check for that here.
-
- // flag that this has not yet successfully installed
- installedFlag = false;
-
- // don't install if the Option key is depressed
- if (KeyIsDown(kOptionKeyCode))
- goto Bail;
-
-
- // check that System 7 is available
- retCode = Gestalt(gestaltSystemVersion, &gestaltResponse);
- if (retCode != noErr || gestaltResponse < 0x0700)
- goto Bail;
-
- // find if my global struct already exists
- retCode = Gestalt(kReinstInitSelector, &gestaltResponse);
-
- if (retCode != noErr || gestaltResponse == 0) {
-
- // this is the first installation
-
- // install Gestalt selector to return handle of our globals
- retCode = NewGestalt(kReinstInitSelector,
- ReinstInitGestaltDispatch);
- if (retCode != noErr) goto Bail;
-
- // no existing globals, so allocate them
-
- gHandle = (GlobalsHandle) NewHandleSys(sizeof(Globals));
- if (gHandle == nil) goto Bail;
-
- // record the size of the globals
- (**gHandle).globalsSize = sizeof(Globals);
-
- // since this is the first install, this INIT's handle
- // must never be disposed to protect our dispatchers
-
- (**gHandle).initHandle = nil;
-
- // we are not reinstalling the INIT
- firstInstallFlag = true;
- }
- else {
-
- // this is a reinstallation
-
- // get the handle for the existing globals
- gHandle = (GlobalsHandle) gestaltResponse;
-
- // ensure that the globals block is the current size
-
- if ((**gHandle).globalsSize != sizeof(Globals)) {
-
- SetHandleSize((Handle) gHandle, sizeof(Globals));
- retCode = MemError();
- if (retCode != noErr) goto Bail;
- }
-
- // update or reset any globals here
-
- // get rid of the last incarnation of the INIT code
- // (unless it was the first)
-
- previousInitHandle = (**gHandle).initHandle;
- if (previousInitHandle != nil)
- DisposeHandle(previousInitHandle);
-
- // save the handle to this version of the INIT
-
- (**gHandle).initHandle = initHandle;
-
- // we are reinstalling the INIT
- firstInstallFlag = false;
- }
-
- // okay, we're committed to installation
- installedFlag = true;
-
- // make the INIT float free
- DetachResource(initHandle);
- HLock(initHandle);
-
- // save the globals handle into the GetGlobalsHandleFromStorage code
- (void) GetGlobalsHandleFromStorage(gHandle);
-
- // patch _Pack3 trap (Standard File Package)
-
- if (firstInstallFlag) {
-
- // save the old trap address, then point the trap at my dispatch
- // function
-
- (**gHandle).pack3TrueAddr = (ProcPtr) GetToolTrapAddress(_Pack3);
- SetToolTrapAddress((long) Pack3PatchDispatch, _Pack3);
- }
-
- // store the address of my patch in the globals
- // so the dispatcher knows where to go
-
- (**gHandle).pack3PatchAddr = Pack3Patch;
-
- // store the address of my gestalt function in the globals
- (**gHandle).reinstInitGestaltFunctionAddr = ReinstInitGestaltFunction;
-
-
-
- Bail:
-
- // could do a ShowINIT here based on the installedFlag
- if (!installedFlag) DebugStr("\p Reinstallable INIT installation failed");
- }
-
-
- // Dispatch routines
- //
- // These routines are the targets of any trap and hook addresses we
- // installed at INIT time (the Gestalt function as well)
- //
- // This code cannot change or move until reboot. We will strand the
- // first version of the INIT code that is installed to ensure
- // that these routines remain present. These routines are not
- // used in the reinstalled code (i.e. the routines from the first
- // install will continue to be called)
- //
- // If a lot of trap patches are in place, a more general dispatcher
- // might be appropriate
-
- void ReinstInitGestaltDispatch()
- {
- // dispatch to my Gestalt routine
-
- // destroys D0, A0
- asm {
- MOVE.L #0, -(SP) ; parameter to GetGlobalsHandle...
- JSR GetGlobalsHandleFromStorage
- ADDQ.L #4, SP ; dispose of parameter
- MOVE.L D0, A0 ; deref the globals handle
- MOVE.L (A0), A0 ; to get the routine address
- MOVE.L Globals.reinstInitGestaltFunctionAddr(A0), A0
- JMP (A0) ; jump to the routine
- }
- }
-
- void Pack3PatchDispatch()
- {
- // dispatch to my Pack3 trap patch
- // destroys A0, D0
- asm {
- MOVE.L #0, -(SP)
- JSR GetGlobalsHandleFromStorage
- ADDQ.L #4, SP
- MOVE.L D0, A0
- MOVE.L (A0), A0
- MOVE.L Globals.pack3PatchAddr(A0), A0;
- JMP (A0)
- }
- }
-
- //
- // Patch routines and other meat code
- //
-
- void Pack3Patch()
- {
- // patch to Standard File
- // just beeps before the dialog is raised
-
- SysBeep(10);
-
- asm {
- // get the address of the real standard file trap
- // and jump to it
-
- MOVE.L #0, -(SP) ; parameter to GetGlobalsHandle...
- JSR GetGlobalsHandleFromStorage
- ADDQ.L #4, SP ; dispose of parameter
- MOVE.L D0, A0 ; deref the globals handle
- MOVE.L (A0), A0 ; to get the real trap address
- MOVE.L Globals.pack3TrueAddr(A0), A0
- JMP (A0) ; jump to it
- }
- }
-
- // this Gestalt function returns the address of the globals block
- pascal OSErr ReinstInitGestaltFunction (OSType selector, long * response)
- {
- *response = (long) GetGlobalsHandleFromStorage(nil);
-
- return noErr;
- }
-
-
- // Keith Rollin's utility for using GetKeys from C
- // returns true if the Key with the given KeyCode is down
-
- Boolean KeyIsDown(short keyCode)
- {
- union {
- KeyMap asMap;
- unsigned char asBytes[16];
- } myMap;
-
- GetKeys(myMap.asMap);
- return ((myMap.asBytes[keyCode >> 3] >>
- (keyCode & 0x07)) & 1) != 0;
- }
-
- // GetGlobalsHandleFromStorage lets us magically store a handle
- // in the code's block of memory
- //
- // passing in a value for gHandle saves the value; calling this
- // routine with nil later retrieves the value
-
- GlobalsHandle GetGlobalsHandleFromStorage(GlobalsHandle gHandle)
- {
- GlobalsHandle *gHandlePtr;
-
- // get the address of our handle storage space
-
- asm {
- BSR @1 ; push the address of the saved space
-
- DC.L 0 ; the saved handle will go here
-
- @1
- MOVE.L (SP)+, gHandlePtr ; pop the address off of the stack
- }
-
- // use the pointer to store the handle if we were passed a handle
- if (gHandle) *gHandlePtr = gHandle;
-
- // return the handle that is there now
- return *gHandlePtr;
- }
-